{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "a96800b7",
   "metadata": {},
   "source": [
    "\n",
    "Python file for GoogleCollab ant jupytext (pair file .ipynb)\n",
    "Original file is located at https://github.com/wwwmyroot/nn_auto_numbers/NN_auto_numbers_MAIN.py\n",
    "\n",
    "LATER: Добавить YOLO_v8"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "af8efe72",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title ---- TRY: решение дисконнекта GoogleCollab\n",
    "# ---- Не понадобилось, но сохраню на всякий случай ----\n",
    "# -- v0\n",
    "# from IPython.display import Audio\n",
    "#   # import numpy as np\n",
    "#\n",
    "# Audio(np.array([0] * 2 * 3600 * 3000, dtype=np.int8), normalize=False, rate=3000, autoplay=True)\n",
    "#   # Audio([None] * 2 * 3600 * 3000, normalize=False, rate=3000, autoplay=True)\n",
    "#\n",
    "# -- v1: F-12 and input in dev-tools console\n",
    "#\n",
    "# function ClickConnect(){\n",
    "#  console.log(\"Working\");\n",
    "#  document.querySelector(\"colab-toolbar-button#connect\").click()\n",
    "#}\n",
    "#setInterval(ClickConnect, 60000)\n",
    "#\n",
    "# -- v2: python solution\n",
    "# import numpy as np\n",
    "# import time\n",
    "# import mouse\n",
    "# while True:\n",
    "    # random_row = np.random.random_sample()*100\n",
    "    # random_col = np.random.random_sample()*10\n",
    "    # random_time = np.random.random_sample()*np.random.random_sample() * 100\n",
    "    # mouse.wheel(1000)\n",
    "    # mouse.wheel(-1000)\n",
    "    # mouse.move(random_row, random_col, absolute=False, duration=0.2)\n",
    "    # mouse.move(-random_row, -random_col, absolute=False, duration = 0.2)\n",
    "    # mouse.LEFT\n",
    "    # time.sleep(random_time)\n",
    "#"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6061304f",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title ---- Создать инфраструктуру папок. Разово.\n",
    "# -- папка \"my_yolov5\" для хранения моделей и данных\n",
    "# -- v0: python lib (base commands for operating in OS);\n",
    "import os\n",
    "# -- Для работы с архивами в питоне\n",
    "import zipfile\n",
    "#\n",
    "#\n",
    "# полный путь к папке в переменной TRAIN_DIR. Будет использоваться и в других функциях\n",
    "TRAIN_DIR = \"/content/my_yolov5\"\n",
    "# при помощии команды \"mkdir\" создадим новую папку\n",
    "os.mkdir(TRAIN_DIR)\n",
    "print(\"-- Создан каталог  my_yolov5  для хранения моделей и данных.\")\n",
    "# посмотреть содержание каталога\n",
    "print(\"-- Текущее содержание корневого рабочего каталога:\")\n",
    "print(os.listdir(\"/content/\"))\n",
    "#\n",
    "# -- v1: напрямую\n",
    "# NOTE: Лучше всё равно средствами python, т.к. переменная \"TRAIN_DIR\"\n",
    "# будет использоваться и в других операциях;\n",
    "#\n",
    "#!ls -la\n",
    "#!rm -rf /content/sample_data\n",
    "#!ls -la\n",
    "#!rm -rf /content/my_yolov5\n",
    "#!ls -la\n",
    "# ---- аналог:\n",
    "#!mkdir {TRAIN_DIR}\n",
    "#!mkdir my_yolov5\n",
    "# --- control:\n",
    "# print(os.listdir(\"/content/\"))\n",
    "#!ls -la\n",
    "#"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1d3bb57c",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title ---- Загрузка подготовленного датасета (gdown; yolo.zip)\n",
    "#\n",
    "# -- загрузка при помощи gdown\n",
    "#!gdown https://storage.yandexcloud.net/terradev/terrayolo/cabin_data_1_classes.zip\n",
    "# ---- UNCOMMENT:\n",
    "#!gdown https://storage.yandexcloud.net/aiueducation/marketing/datasets/yolo.zip\n",
    "# gdown.download (\"https://storage.yandexcloud.net/aiueducation/marketing/datasets/yolo.zip\")\n",
    "print(\"-- Датасет успешно загружен.\")\n",
    "#\n",
    "# -- control:\n",
    "#!ls -la\n",
    "os.listdir(\"/content/\")\n",
    "#"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "66c9aeab",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title ---- Распаковка датасета (.zip)\n",
    "#\n",
    "# -- средствами питона\n",
    "try:\n",
    "    with zipfile.ZipFile(\"yolo.zip\", \"r\") as zip_ref:\n",
    "        zip_ref.extractall(\"/content/my_yolov5\")\n",
    "except zipfile.BadZipFile:\n",
    "    print(\"(!)ERROR: Файл архива поврежден или не является архивом.\")\n",
    "#\n",
    "print(\"-- Датасет успешно распакован.\")\n",
    "# -- control:\n",
    "#!ls {TRAIN_DIR}\n",
    "print(os.listdir(TRAIN_DIR))\n",
    "#\n",
    "# -- в коллабе:\n",
    "#!unzip -q -o yolo.zip  -d {TRAIN_DIR}\n",
    "# - OR:\n",
    "#!unzip -q -o /content/yolo.zip  -d {TRAIN_DIR}\n",
    "#!unzip -q -o \"yolo.zip\" -d /content/my_yolov5\n",
    "#\n",
    "#!unzip --h\n",
    "#  -q: \"тихий режим\" min сообщений;\n",
    "#  -o: режим перезаписи, на случай, если по путям распаковки существуют другие файлы;\n",
    "#  -d: целевая директория распаковки;\n",
    "#\n",
    "# -- CLI python:\n",
    "# python -m zipfile -e chest_xray.zip /content/data/\n",
    "#"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0e331bdf",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title ---- директории для train и valid\n",
    "#\n",
    "# в каталогах train и valid - тренировочная и валидационная части датасета;\n",
    "# -- сохранить путь к файлу в отдельную переменную;\n",
    "data_file_path = TRAIN_DIR+\"dataset.yaml\"\n",
    "# через cat посмотрим содержимое файла data.yaml;\n",
    "print(\"-- содержимое файла data.yaml (тренировочные и валидационные части)\")\n",
    "#!cat {data_file_path}\n",
    "with open(data_file_path, \"r\") as f:\n",
    "    print(f.read())\n",
    "#\n",
    "#  посмотреть папку тренировочной выборки;\n",
    "print(\"-- Папка тренировочной выборки:\")\n",
    "#!ls {TRAIN_DIR+\"train\"}\n",
    "print(os.listdir(TRAIN_DIR+\"train\"))\n",
    "#\n",
    "# ---- control:\n",
    "print(\"-- Папка \", TRAIN_DIR+\"train/labels :\")\n",
    "#!ls {TRAIN_DIR+\"train\"}/labels\n",
    "print(os.listdir(TRAIN_DIR+\"train/labels\"))\n",
    "print(\"-- Папка \", TRAIN_DIR+\"train/images :\")\n",
    "#!ls {TRAIN_DIR+\"train/images/\"}\n",
    "print(os.listdir(TRAIN_DIR+\"train/images/\"))\n",
    "#\n",
    "# ---- control:\n",
    "# сколько картинок / сколько описаний\n",
    "# NOTE: количества могут не совпадать, это нормально, так как номера могут быть не на всех картинках).\n",
    "#\n",
    "print(\"---- количество картинок: \", len(os.listdir(TRAIN_DIR+\"train/images/\")))\n",
    "print(\"---- количество описаний: \", len(os.listdir(TRAIN_DIR+\"train/labels/\")))\n",
    "#\n",
    "# ---- control\n",
    "# что содержится в описаниях в валидационной части;\n",
    "# возьмем имя первого файла из подкаталога labels\n",
    "lab_f_name = os.listdir(TRAIN_DIR+\"train/labels/\")[0]\n",
    "#!cat {TRAIN_DIR+\"train/labels/\"+lab_f_name}\n",
    "with open(TRAIN_DIR+\"train/labels/\"+lab_f_name, \"r\") as f:\n",
    "    print(f.read())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7ed889d0",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title ---- Модель.\n",
    "#\n",
    "# -- загрузка пакета TerraYolo\n",
    "#!pip install --index-url https://test.pypi.org/simple/ --no-deps TerraYolo --upgrade\n",
    "# -- TO UNCOMMENT:\n",
    "!pip install --upgrade --force-reinstall --index-url https://test.pypi.org/simple/ --no-deps TerraYoloTest\n",
    "#\n",
    "# -- Импорт класса TerraYoloV5 из пакета TerraYolo модуля TerraYoloTest;\n",
    "from TerraYoloTest.TerraYolo import TerraYoloV5\n",
    "#\n",
    "# -- создать экземпляр класса my_terra_yolov5, в качестве параметра - передать путь к рабочему каталогу;\n",
    "my_terra_yolov5 = TerraYoloV5(work_dir=TRAIN_DIR)\n",
    "#\n",
    "# При создании класса в папку рабочем каталоге создается директория yolov5,\n",
    "# в которую загружаются скрипты модели;\n",
    "print(\"-- Папка для скриптов модели:\")\n",
    "#!ls {my_terra_yolov5.work_dir}\n",
    "print(os.listdir(TRAIN_DIR))\n",
    "#\n",
    "# Метод count_labels может посчитать число объектов в файлах меток.\n",
    "# В качестве параметра ему нужно передать путь к yaml-описанию датасета.\n",
    "# Проверим число объектов датасета:\n",
    "print(\"-- Число объектов датасета:\")\n",
    "my_terra_yolov5.count_labels(data_file_path)\n",
    "#\n",
    "# -- по итогам у нас: 532 тренировочные метки ; 3 валидационные метки;\n",
    "#"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "75063ebc",
   "metadata": {},
   "outputs": [],
   "source": [
    "# @title ---- Обучение.\n",
    "#\n",
    "# YOLOv5 используется в трех основных режимах - тренировка, валидация и детекция.\n",
    "# Они отличаются способами загрузки данных.\n",
    "# Загрузчик режима train спроектирован как оптимальный вариант с точки зрения скорости загрузки данных и качества обучения.\n",
    "# Режим валидации разработан c целью быстро рассчитать метрики обучения,\n",
    "# а режим детекции - для лучших результатов в реальных задачах.\n",
    "#\n",
    "# -- Основной словарь обучения\n",
    "# Для начала достаточно указать количество эпох и путь к файлу с описанием датасета;\n",
    "#\n",
    "train_dict=dict()\n",
    "# количество эпох;\n",
    "train_dict[\"epochs\"] = 10\n",
    "# путь к описанию датасета\n",
    "train_dict[\"data\"] = data_file_path\n",
    "#\n",
    "# ---- control\n",
    "# файл с описанием путей к данным;\n",
    "print (\"-- Файл с описанием путей к данным:\")\n",
    "#!cat {data_file_path}\n",
    "with open(data_file_path, \"r\") as f:\n",
    "    print(f.read())\n",
    "#\n",
    "# NOTE: указывать относительные пути вместо абсолютных удобно для переносимости;\n",
    "#\n",
    "# -- в режиме 'train' запустить обучение с задаными основным словарем параметрами при помощи метода \"run\";\n",
    "#\n",
    "my_terra_yolov5.run(train_dict, exp_type=\"train\")\n",
    "#"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8fba3bc2",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title ---- Детекция.\n",
    "#\n",
    "# Проверить результаты.\n",
    "# Снова при помощи словаря, настроить параметры детектора.\n",
    "# Модель возвращает вероятности обнаружения объекта.\n",
    "# Указать порог уверенности 'conf'.\n",
    "# Нужно также указать путь к весам обученной модели.\n",
    "# По умолчанию результаты всех экспериментов хранятся в подкаталоге runs каталога модели.\n",
    "#\n",
    "# ---- control\n",
    "# каталог с результатами экспериментов;\n",
    "# для первого эксперимента в папке создается каталог 'exp',\n",
    "# для следующего 'exp2', потом 'exp3' и т.д.;\n",
    "print(\"---- Каталог с результатами экспериментов:\")\n",
    "#!ls /content/my_yolov5/yolov5/runs\n",
    "print(os.listdir(\"/content/my_yolov5/yolov5/runs\"))\n",
    "# каталог с результатами обучения;\n",
    "print(\"---- Каталог с результатами обучения:\")\n",
    "#!ls /content/my_yolov5/yolov5/runs/train/\n",
    "print(os.listdir (\"/content/my_yolov5/yolov5/runs/train/\"))\n",
    "#\n",
    "# веса модели: best.pt (лучшие результаты на валидации),\n",
    "# last.pt (рассчитанные по последней эпохе обучения);\n",
    "print(\"---- Веса модели:\")\n",
    "#!ls /content/my_yolov5/yolov5/runs/train/exp/weights\n",
    "print(os.listdir (\"/content/my_yolov5/yolov5/runs/train/exp/weights\"))\n",
    "#"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8d655dee",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title ---- Настройки валидации\n",
    "# Параметры моделей можно посмотреть в словаре exp_dict.\n",
    "# Для каждого типа эксперимента в этом словаре хранится отдельный словарь,\n",
    "# доступный по ключам [\"train\"], [\"val\"], [\"test\"].\n",
    "#\n",
    "test_dict = dict()\n",
    "# так как в нашем датасете мы не подготовили тестовую выборку -  укажем путь\n",
    "# к изображениям валидационной выборки;\n",
    "test_dict[\"source\"] = TRAIN_DIR+\"/valid/images/\"\n",
    "# порог вероятности обнаружения объекта\n",
    "test_dict[\"conf\"] = 0.5\n",
    "# путь к весам модели\n",
    "test_dict[\"weights\"] = my_terra_yolov5.exp_dict[\"train\"][\"last_exp_path\"]+\"/weights/best.pt\"\n",
    "#"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7ccc8191",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title ---- Запуск.\n",
    "print(\"---- START ----\")\n",
    "my_terra_yolov5.run(test_dict, exp_type=\"test\")\n",
    "print(\"\\n ---- FINISH ----\")\n",
    "#"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "40a4a1c6",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title ---- Просмотр результатов детекции;\n",
    "#\n",
    "# зафиксируем в константе число изображений:\n",
    "N_SAMPLES=3\n",
    "# показать n_samples изображений:\n",
    "print(\"-- Примеры изображений:\")\n",
    "my_terra_yolov5.show_test_images(n_samples=N_SAMPLES, img_dir=None)\n",
    "#\n",
    "# Результаты по умолчанию хранятся в подкаталоге test каталога run.\n",
    "# Метод show_test_images покажет из каталога img_dir n_samples случайных\n",
    "# изображений, но если каталог не указывать, то метод попытается найти\n",
    "# результаты последней детекции."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b3569a37",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b7c48106",
   "metadata": {
    "lines_to_next_cell": 2
   },
   "outputs": [],
   "source": [
    "#@title ---- FIX_#0104: \"last_exp_path\"\n",
    "# бывает, что колаб к этому шагу профукивает путь к последнему эксперименту.\n",
    "# путь хранится в скриптах для train, detect, validate запуска самого фреймворка\n",
    "# если нет смысла перезапускать предыдущие шаги или копаться во фреймверке, то\n",
    "# есть смысл явно прописывать пути, заново объявить пути к рабочим\n",
    "# папкам итренировочному словарю.\n",
    "# NOTE: обратить внимание на номер папки 'exp' (exp; exp2; exp3 и т.д.).\n",
    "# ---- uncomment FIX_№0104 (start):\n",
    "#f TRAIN_DIR = \"/content/my_yolov5\"\n",
    "#f data_file_path = TRAIN_DIR+\"/dataset.yaml\"\n",
    "#f train_dict=dict()\n",
    "# - количество эпох;\n",
    "#f train_dict['epochs'] = 10\n",
    "# - путь к описанию датасета\n",
    "#f train_dict['data'] = data_file_path\n",
    "#f train_dict['weights'] =  \"/content/my_yolov5/yolov5/runs/train/exp/weights/last.pt\"\n",
    "#- control\n",
    "# файл с описанием путей к данным;\n",
    "#f print (\"-- Файл с описанием путей к данным:\")\n",
    "!cat {data_file_path}\n",
    "#f with open(data_file_path, \"r\") as f:\n",
    "#f    print(f.read())\n",
    "#\n",
    "# ---- FIX (end)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5daa2d7f",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title ---- Дообучение.\n",
    "#\n",
    "# Неплохо, но попробуем улучшить.\n",
    "# Для того, чтобы не повторять обучение с самого начала - взять веса,\n",
    "# полученные на последней эпохе обучения.\n",
    "#\n",
    "# -- используются веса последней эпохи;\n",
    "#\n",
    "train_dict[\"weights\"] = my_terra_yolov5.exp_dict[\"train\"][\"last_exp_path\"]+\"/weights/last.pt\"\n",
    "#fix#0104: train_dict['weights'] =  \"/content/my_yolov5/yolov5/runs/train/exp/weights/last.pt\"\n",
    "#\n",
    "# -- NOTE: если collab к этому шагу опять профукал 'last_exp_path', см. FIX_#0104.\n",
    "# плюс, скорее всего он профукал и словарь обучения и путь к тренировояной папке.\n",
    "# NOTE: обратить внимание на номер папки 'exp' (exp; exp2; exp3 и т.д.).\n",
    "#\n",
    "# -- control\n",
    "print(\"-- Веса:\")\n",
    "print(train_dict[\"weights\"])\n",
    "print(\"-- -- -- --\")\n",
    "#\n",
    "# запуск скрипта train  с параметрами train_dict;\n",
    "my_terra_yolov5.run(train_dict, exp_type=\"train\")\n",
    "##"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "056a4660",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title ---- Детекция. Следующий цикл.\n",
    "#\n",
    "#\n",
    "# -- NOTE: если collab к этому шагу опять профукал 'last_exp_path',\n",
    "# то он профукал и словарь обучения и путь к тренировояной папке.\n",
    "# см. FIX_#0104\n",
    "# NOTE: обратить внимание на номер папки 'exp' (exp; exp2; exp3 и т.д.).\n",
    "#\n",
    "# путь к лучшим весам модели;\n",
    "test_dict[\"weights\"] = my_terra_yolov5.exp_dict[\"train\"][\"last_exp_path\"]+\"/weights/best.pt\"\n",
    "#fix#0104: test_dict['weights'] =  \"/content/my_yolov5/yolov5/runs/test/exp/weights/best.pt\"\n",
    "#\n",
    "# проверим полный путь;\n",
    "print(test_dict[\"weights\"])\n",
    "#\n",
    "#запускаем скрипт test с параметрами test_dict;\n",
    "my_terra_yolov5.run(test_dict, exp_type=\"test\")\n",
    "#"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "134a6fc6",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title ---- Просмотр результатов (пути к файлам изменились).\n",
    "#\n",
    "# -- зафиксируем в константе число изображений;\n",
    "N_SAMPLES=3\n",
    "# -- показать n_samples изображений;\n",
    "print(\"-- Примеры изображений:\")\n",
    "my_terra_yolov5.show_test_images(n_samples=N_SAMPLES, img_dir=None)\n",
    "#"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fd06578f",
   "metadata": {},
   "outputs": [],
   "source": [
    "# @title ---- Понижение порога детекции (если не все машинки определились) -- NOTE: НЕ ПОНАДОБИЛОСЬ;\n",
    "#\n",
    "# -- NOTE: НЕ ПОНАДОБИЛОСЬ, после дообучения обнаружились все (тоность min 0,71 ; max 0,85);\n",
    "#\n",
    "# --установить значение\n",
    "test_dict[\"conf\"] = 0.4\n",
    "#\n",
    "# -- запуск\n",
    "my_terra_yolov5.run(test_dict, exp_type=\"test\")\n",
    "#\n",
    "# -- просмотр\n",
    "my_terra_yolov5.show_test_images(n_samples=N_SAMPLES)\n",
    "#"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fa956fdd",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title ---- Валидация.\n",
    "#\n",
    "# при помощи словаря настроим параметры и посмотрим результаты валидации;\n",
    "val_dict=dict()\n",
    "val_dict[\"data\"] = train_dict[\"data\"]\n",
    "#\n",
    "val_dict[\"weights\"] = os.path.abspath(\n",
    "        my_terra_yolov5.exp_dict[\"train\"][\"last_exp_path\"]+\"/weights/last.pt\")\n",
    "#\n",
    "# -- control path;\n",
    "print(\"-- Путь к весам:\")\n",
    "print(val_dict[\"weights\"])\n",
    "#\n",
    "# -- запуск;\n",
    "my_terra_yolov5.run(val_dict, exp_type=\"val\")\n",
    "#\n",
    "# Результаты валидации сохраняются в соответствующем подкаталоге каталога runs;\n",
    "os.listdir(my_terra_yolov5.exp_dict[\"train\"][\"last_exp_path\"])\n",
    "#"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "36f4a5ae",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title ---- Матрица ошибок.\n",
    "#\n",
    "# Анализ матрицы ошибок (матрица путаницы).\n",
    "# Каждому классу соответствует строка матрицы и можно увидеть,\n",
    "# за объекты каких классов модель принимает объекты определенного класса.\n",
    "# Так как у в нашем датасете только один класс, модель может просто не\n",
    "# распознать объект и в этом случае можно сказать, что она отнесла изображение\n",
    "# к классу 'фон'. Вывести на экран список файлов из любого каталога\n",
    "# можно при помощи метода show_val_results.\n",
    "# Если не указать каталог - по умолчанию модель попытается найти изображения\n",
    "# в подкаталоге 'val' каталога 'runs'\n",
    "#\n",
    "my_terra_yolov5.show_val_results(img_path=None, img_list=[\"confusion_matrix.png\", \"PR_curve.png\"])\n",
    "#"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "238a7244",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title ---- Сохранение модели. GoogleDrive.\n",
    "#\n",
    "# Чтобы иметь возможность использовать результаты обучения\n",
    "# достаточно сохранить веса модели в какое-нибудь постоянное хранилище.\n",
    "# Диск колаба станет недоступен по завершению сессии, поэтому сначала подключим свой google drive.\n",
    "# Если запрещены всплывающие окна нужно разрешить их для colab.\n",
    "# И нужно подтвердить разрешение на доступ к диску.\n",
    "#\n",
    "# -- подключениe google drive;\n",
    "# импорт модуля:\n",
    "from google.colab import drive\n",
    "# монтировать свой диск в папку drive рабочего каталога;\n",
    "drive.mount(\"/content/drive/\")\n",
    "#\n",
    "# Посмотрим, что у нас получилось. В папке /content/drive должен появиться каталог MyDrive - это и есть наш диск google.\n",
    "#\n",
    "# -- control\n",
    "# В папке /content/drive должен появиться каталог MyDrive.\n",
    "# Это и есть наш диск google.\n",
    "# сохранить путь к нашему диску в переменную:\n",
    "my_drv_path = \"/content/drive/MyDrive/\"\n",
    "#\n",
    "# содержимое каталога диска:\n",
    "print(\"-- В папке /content/drive должен появиться каталог MyDrive :\")\n",
    "#!ls {my_drv_path}\n",
    "print(os.listdir(\"/content/drive/\"))\n",
    "# создать каталог для хранения нашей модели:\n",
    "#!mkdir {my_drv_path}/yolo_weights/\n",
    "os.mkdir(\"/content/drive/MyDrive/yolo_weights\")\n",
    "print(\"-- Создан кататлог для хранения модели:\")\n",
    "print(os.listdir(\"/content/drive/MyDrive\"))\n",
    "#\n",
    "# сохраним путь к лучшим весам в переменную\n",
    "best_weights_path = my_terra_yolov5.exp_dict[\"train\"][\"last_exp_path\"]+\"/weights/best.pt\"\n",
    "# сохраним путь к последним весам в переменную\n",
    "last_weights_path = my_terra_yolov5.exp_dict[\"train\"][\"last_exp_path\"]+\"/weights/last.pt\"\n",
    "# скопируем лучшие веса\n",
    "#!cp {best_weights_path} {my_drv_path+\"/yolo_weights/\"}\n",
    "cmd_cp_01 = f\"cp {best_weights_path} {my_drv_path+'/yolo_weights/'}\"\n",
    "os.system(cmd_cp_01)\n",
    "print(\"-- best_weights: Скопированы.\")\n",
    "# скопируем последние веса\n",
    "#!cp {last_weights_path} {my_drv_path+\"/yolo_weights/\"}\n",
    "cmd_cp_02 = f\"cp {last_weights_path} {my_drv_path+'/yolo_weights/'}\"\n",
    "os.system(cmd_cp_02)\n",
    "print(\"-- last_weights: Скопированы.\")\n",
    "#\n",
    "# control\n",
    "print(\"-- Содержание папки с весами на my drive: \")\n",
    "#!ls {my_drv_path+\"/yolo_weights/\"}\n",
    "# print(os.listdir({my_drv_path+\"/yolo_weights/\"}))\n",
    "print(os.listdir(\"/content/drive/MyDrive/yolo_weights/\"))\n",
    "#"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5d4acde2",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title ---- Про варианты YOLO. Аннотация модели.\n",
    "#\n",
    "# YOLO v5 позволяет сделать выбор из нескольких архитектур, отличающихся сложностью,\n",
    "# числом параметров и как следствие скоростью работы.\n",
    "# Увеличение качества распознавания обычно сопровождается увеличением размера модели,\n",
    "# и ее соответствующего замедленния. Отличия разных архитектур можно отследить в таблице.\n",
    "#\n",
    "# По умолчанию YOLO v5 использует модификацию yolov5s.\n",
    "# При помощи метода get_annotation можно познакомиться с структурой модели.\n",
    "#\n",
    "# -- вывести файл аннотации для модели\n",
    "print(\"-- Аннотация для модели:\")\n",
    "my_terra_yolov5.get_annotation(\"yolov5s\")\n",
    "print(\"-- NOTE: памятка - закомментироана в коде ячейки.\")\n",
    "print(\"---- FIN ----\")\n",
    "#\n",
    "#\n",
    "# Cообщения модели.\n",
    "# Перед стартом обучения модель информирует о состоянии сервисов и параметров, в первую очередь\n",
    "# нас будут интересовать метрики обучения, которые выводятся каждую эпоху в строке:\n",
    "#\n",
    "# **'Class     Images  Instances          P          R      mAP50   mAP50-95'**\n",
    "#\n",
    "# Колонки  **Class,     Images,  Instances**\n",
    "# относятся к статистике классов обучающей выборки, можете свериться с нашими данными.\n",
    "# В колонках  '** P          R      mAP50   mAP50-95**\n",
    "# выводятся значения метрик на каждой эпохе.\n",
    "#\n",
    "# ### P - Precision, R-Recall, Accuracy\n",
    "#\n",
    "# Когда модель делает прогноз, может реализоваться одна из четырех комбинаций правильного\n",
    "# ответа и предсказания модели:\n",
    "# - TP = True Positive: объект действительно есть и модель его правильно обнаруживает\n",
    "# - TN = True Negative: объект отсутствует и детектор его правильно не находит\n",
    "# - FP = False Positive: объект отсутствует, но модель ошибочно его детектирует\n",
    "# - FN = False Negative: объект имеется, но модель ошибочно его не видит\n",
    "#\n",
    "# ${Precision}=\\frac{TP}{TP+FP}$ - в числителе у нас число правильных положительных ответов,\n",
    "# а в знаменателе - число всех **ПОЛОЖИТЕЛЬНЫХ ОТВЕТОВ** модели: и верных и неверных.\n",
    "# Таким образом **Precision** характеризует точность детекции положительного класса.\n",
    "# Например, пусть мы диагностируем заболевание и класс Positive\n",
    "# соответствует факту заболевания (что должно вызвать недумение,  так как в болезни\n",
    "# обычно трудно найти что-нибудь положительное) - тогда **Presision** равняется\n",
    "# вероятности обнаружить болезнь, **если пациент действительно болен**.\n",
    "#\n",
    "# ${Recall}=\\frac{TP}{TP+FN}$ - в числителе у нас снова число правильных положительных\n",
    "# ответов, а в знаменателе в этот раз - число всех **ПОЛОЖИТЕЛЬНЫХ ИСХОДОВ**.\n",
    "# В примере с установкой диагноза  **Recall** будет просто равняться вероятности\n",
    "# обнаружить заболевание.\n",
    "#\n",
    "# Таким образом, **Precision** измеряет какой процент предсказаний корректен,\n",
    "# а **Recall** измеряет насколько хорошо модель находит положительные образцы\n",
    "# (в нашем примере - ставит диагнозы).\n",
    "#\n",
    "# Cледует также упомянуть про **Accuracy** (если бы мы выше перевели Precision\n",
    "# как Точность, Accuracy следовало бы перевести как Аккуратность):\n",
    "#\n",
    "# ${Accuracy}=\\frac{TP+TN}{TP+FP+TN+FN} $\n",
    "#\n",
    "# Рассмотренные метрики хороши, но в случае дисбаланса в датасете могут потерять\n",
    "# свою информативность. На примере Accuracy, рассмотрим ситуацию когда у нас\n",
    "# среди 1000 здоровых пациентов найдется один заболевший. В этом случае, для\n",
    "# модели очень велик соблазн классифицировать всех здоровыми, при этом получится\n",
    "# великолепные Precision и Accuracy = 0.999, но задача постановки диагноза\n",
    "# выполнена не будет, Recall будет равен 0. Если поступить наоборот и максимизировать\n",
    "# Recall - модель будет стремиться выявить как можно больше больных и уменьшится\n",
    "# точность прогноза. Поэтому были разработаны и используются другие показатели.\n",
    "#\n",
    "# ### IoU - Interception over Union\n",
    "#\n",
    "# Чтобы лучше понять следующие метрики нам нужно познакомиться с одним важным\n",
    "# показателем, заодно подробнее рассмотрим процесс детекции:  YoloV5 использует\n",
    "# метрику Interception over Union (aka IoU, индекс (расстояние) Жаккара,\n",
    "# пересечение над объединением).\n",
    "#\n",
    "# Предположим верхнюю рамку построил детектор, а нижняя - действительный bbox объекта.\n",
    "# Отношение площадей пересечения и объединения  прямоугольников всегда меньше\n",
    "# или равно 1. Далее достаточно назначить порог, при превышении которого\n",
    "# факт можно подтверждать детекцию.\n",
    "#\n",
    "# На самом деле, до расчета IoU, YOLO предсказывает вероятность обнаружить объект\n",
    "# Conf, поэтому обычно настраиваются два порога -  для IoU  и для Conf.\n",
    "#\n",
    "# ### AP - Average Precision\n",
    "#\n",
    "# Чтобы посчитать следующую метрику -  Average Precision рассмотрим упрощенный\n",
    "# пример из отличной статьи господина Kukil, полный текст которой можно найти по ссылке:\n",
    "# https://learnopencv.com/mean-average-precision-map-object-detection-model-evaluation-metric/\n",
    "# Предположим наш детектор обнаружил на изображении объекты классов\n",
    "# 'dog', 'person', 'teddy' и других, но нас пока будут интересовать только класс 'dog'n.\n",
    "# Можем сразу отметить, что некоторые объекты классифицированы ошибочно.\n",
    "#\n",
    "# Отсортируем все найденные объекты класса dog в порядке возрастания вероятности детекции Conf:\n",
    "#\n",
    "# Во второй колонке таблицы у нас порог уверенности, в третьей - результат детекции, в четвертой\n",
    "# и пятой - соответственно накопленные TP и FP для всех экпериментов, в 6 и 7 - соответственно\n",
    "# Precision и Recall, которые последовательно, строка за строкой сверху вниз,\n",
    "# рассчитываются после каждой детекции\n",
    "#\n",
    "#\n",
    "# Также отметим факт, что Recall не убывает, а Precision может как расти, так и убывать.\n",
    "# Пока примем к сведению, что в реальности кривую Precision-Recall дополнительно\n",
    "# сглаживают, но принципиально от этого ничего не меняется.\n",
    "#\n",
    "# ### mAP - mean Average Precision,  mAP50, mAP50-95\n",
    "#\n",
    "# Теперь, вычислив значение AP для каждого класса и посчитав среднее арифметическое\n",
    "# для всех классов получим значение метрики mAP (MAP) - Mean Average Precision - усредненная\n",
    "# по классам средняя Precision.\n",
    "# Если для усреднения брать значение Precision для порога IoU = .5 мы получим\n",
    "# метрику mAP50. Чем выше значение этой метрики - тем лучше работает модель с порогом IoU=.5\n",
    "# Также для оценки моделей используется метрика mAP50-95. Для ее расчета применяется\n",
    "# дополнительное усреднение по сетке порогов от 0.5 до 0.95 с шагом 0.05\n",
    "# (0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95).\n",
    "# Эта метрика будет тем выше , чем увереннее (с большей вероятностью) модель детектирует объекты.\n",
    "#\n",
    "# Теперь можно снова посмотреть историю обучения и проанализировать поведение динамику метрик\n",
    "#"
   ]
  }
 ],
 "metadata": {
  "jupytext": {
   "cell_metadata_filter": "title,-all",
   "encoding": "# -*- coding: utf-8 -*-",
   "formats": "ipynb,py",
   "main_language": "python"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
